/*
  Copyright (c) 2014 Dimitrij Gester

  "Smart RC TX" is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  "Smart RC TX" is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General
  Public License along with "Smart RC TX"; if not, write to the
  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  Boston, MA  02111-1307  USA
*/

#ifndef COMPOSITEMIXER_H
#define COMPOSITEMIXER_H

#include <Arduino.h>
#include "Mixer.h"
#include "MixerPersistor.h"

template<uint8_t slots>
class CompositeMixer
{
	public:
	CompositeMixer(Channels<slots> &ch)
	: trim(ch)
	, preScaler(ch)
	, expo(ch)
	, limiter(ch)
	, channels(ch)
	{
		for(int i = 0; i < slots; ++i)
		{
			this->mixer[i] = new CrossMixer<slots>(ch, i);
		}
	}
	
	~CompositeMixer()
	{
		for(int i = 0; i < slots; ++i)
		{
			delete mixer[i];
		}
	}

	void setTrim(uint8_t ch, int16_t sc)
	{
		this->trim.setScale(ch, sc);
	}
	
	void setPreScaler(uint8_t ch, int16_t sc)
	{
		this->preScaler.setScale(ch, sc);
	}
	
	void setExpo(uint8_t ch, int16_t sc)
	{
		this->expo.setScale(ch, sc);
	}
	
	void setCrossMixer(uint8_t src, uint8_t tgt, int16_t scU, int16_t scD)
	{
		this->mixer[src]->setScale(tgt, scU);
		this->mixer[src]->setScaleD(tgt, scD);
	}
	
	void setLimiter(uint8_t ch, int16_t upper, int16_t lower)
	{
		this->limiter.setScale(ch, upper);
		this->limiter.setScaleD(ch, lower);
	}
	
	void apply()
	{
		this->preScaler.mix();
		this->preScaler.apply();

		this->trim.mix();
		this->trim.apply();

		this->expo.mix();
		this->expo.apply();

		for(byte i = 0; i < slots; ++i)
		this->mixer[i]->mix();

		for(byte i = 0; i < slots; ++i)
		this->mixer[i]->apply();
		
		this->channels.constrainChannels(-512, 511);

		this->limiter.mix();
		this->limiter.apply();
	}

	bool performMixerStep()
	{
		static uint8_t calculationStep = 0;
		static uint8_t ch = 0;
		
		switch(calculationStep)
		{
			case 0:
			this->preScaler.mix();
			calculationStep++;
			break;
			case 1:
			this->preScaler.apply();
			calculationStep++;
			break;
			case 2:
			this->trim.mix();
			calculationStep++;
			break;
			case 3:
			this->trim.apply();
			calculationStep++;
			break;
			case 4:
			this->expo.mix();
			calculationStep++;
			break;
			case 5:
			this->expo.apply();
			calculationStep++;
			break;
			case 6:
			if(ch < slots)
			{
				this->mixer[ch++]->mix();
			}
			else
			{
				calculationStep++;
				ch = 0;
			}
			break;
			case 7:
			if(ch < slots)
			{
				this->mixer[ch++]->apply();
			}
			else
			{
				calculationStep++;
				ch = 0;
			}
			break;
			case 8:
			this->channels.constrainChannels(-512, 511);
			calculationStep++;
			break;
			case 9:
			this->limiter.mix();
			calculationStep++;
			break;
			case 10:
			this->limiter.apply();
			calculationStep++;
			break;
		}
		
		if(calculationStep >= 11)
		calculationStep = 0;
		
		return calculationStep == 0;
	}

	uint16_t getPersistSize()
	{
		uint16_t size = 0;
		size += this->preScaler.getPersistSize();
		size += this->trim.getPersistSize();
		size += this->expo.getPersistSize();
		for(byte i = 0; i < slots; ++i)
		size += this->mixer[i]->getPersistSize();

		size += this->limiter.getPersistSize();
		
		return size;
	}
	
	void serialize(MixerPersistor &p)
	{
		this->trim.serialize(p);
		this->preScaler.serialize(p);
		this->expo.serialize(p);
		for(byte i = 0; i < slots; ++i)
		this->mixer[i]->serialize(p);
		this->limiter.serialize(p);
	}
	
	void deserialize(MixerPersistor &p)
	{
		this->trim.deserialize(p);
		this->preScaler.deserialize(p);
		this->expo.deserialize(p);
		for(byte i = 0; i < slots; ++i)
		this->mixer[i]->deserialize(p);
		this->limiter.deserialize(p);
	}
	
	protected:
	TrimMixer<slots> trim;
	ScalerMixer<slots> preScaler;
	ExponentialMixer<slots> expo;
	CrossMixer<slots> *mixer[slots];
	Limiter<slots> limiter;
	Channels<slots> &channels;
};

#endif


